
//
//  YYYTextKitLabel.swift
//  HotNews
//
//  Created by YYY on 2017/11/7.
//  Copyright © 2017年 成品家（北京）网路科技有限公司. All rights reserved.
//

import UIKit

@objc protocol YYYTextKitLabelDelegate: NSObjectProtocol
{
    @objc optional func textKitLabelDidSelectedLinkText(label: YYYTextKitLabel, text: String)
}

class YYYTextKitLabel: UILabel
{
    // MARK: 懒加载属性
    private var selectedRange: NSRange?
    //文本存储
    private lazy var textStorage = NSTextStorage()
    //文本字形布局
    private lazy var layoutManager = NSLayoutManager()
    //文本绘制的范围
    private lazy var textContainer = NSTextContainer()
    
    //链接文本颜色
    var linkTextColor = UIColor.blue
    //选中的背景颜色
    var selectedBackgroudColor = UIColor.lightGray
    //文本代理
    weak var delegate: YYYTextKitLabelDelegate?
    
    // MARK: 构造函数
    override init(frame: CGRect) {
        super.init(frame: frame)
        //准备文本系统
        prepareLabel()
    }
    // MARK: 从xib加载
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        //准备文本系统
        prepareLabel()
    }
    // MARK: 设置文本
    override var text: String?
    {
        didSet
        {
            //更新文本存储
            updateTextStorage()
        }
    }
    // MARK: 设置属性文本
    override var attributedText: NSAttributedString?
    {
        didSet
        {
            //更新文本存储
            updateTextStorage()
        }
    }
    // MARK: 设置字体
    override var font: UIFont!
    {
        didSet
        {
            //更新文本存储
            updateTextStorage()
        }
    }
    // MARK: 设置字体颜色
    override var textColor: UIColor!
    {
        didSet
        {
            //更新文本存储
            updateTextStorage()
        }
    }
    
    // MARK: 布局子视图
    public override func layoutSubviews()
    {
        super.layoutSubviews()
        //设定文本容器大小
        textContainer.size = bounds.size
    }
    
    // MARK: 绘制文本
    public override func drawText(in rect: CGRect)
    {
        let range = NSRange(location: 0, length: textStorage.length)
        
        //绘制背景
        layoutManager.drawBackground(forGlyphRange: range, at: CGPoint())
        //绘制字形
        layoutManager.drawGlyphs(forGlyphRange: range, at: CGPoint())
    }
    
    // MARK: 触摸事件
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        //文本点击位置
        guard let location = touches.first?.location(in: self) else
        {
            return
        }
        selectedRange = linkRangeAtLocation(location)
        //文本点击后属性设置
        modifySelectedTextBackgroud(isSet: true)
    }
    
    public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        //文本点击位置
        guard let location = touches.first?.location(in: self) else
        {
            return
        }
        if let range = linkRangeAtLocation(location)
        {
            if !(range.location == selectedRange?.location && range.length == selectedRange?.length)
            {
                //文本取消点击后属性设置
                modifySelectedTextBackgroud(isSet: false)
                selectedRange = range
                //文本取消点击后属性设置
                modifySelectedTextBackgroud(isSet: true)
            }
        }
        else
        {
            //文本取消点击后属性设置
            modifySelectedTextBackgroud(isSet: false)
        }
        
    }
    
    public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        //文本取消点击后属性设置
        if let range = selectedRange
        {
            let selectedText = (textStorage.string as NSString).substring(with: range)
            delegate?.textKitLabelDidSelectedLinkText?(label: self, text: selectedText)
            
            let when = DispatchTime.now() + 0.25
            DispatchQueue.main.asyncAfter(deadline: when, execute: {
                //文本点击结束后属性设置
                self.modifySelectedTextBackgroud(isSet: false)
            })
        }
    }
    
    //取消点击
    public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        //文本取消点击后属性设置
        modifySelectedTextBackgroud(isSet: false)
    }
}
private extension YYYTextKitLabel
{
    // MARK: 准备文本系统
    func prepareLabel()
    {
        //打开交互
        isUserInteractionEnabled = true
        
        //更新文本存储
        updateTextStorage()
        
        //设置对象之间的关系
        textStorage.addLayoutManager(layoutManager)
        layoutManager.addTextContainer(textContainer)
        
        textContainer.lineFragmentPadding = 0
    }
    
    // MARK: 更新文本存储
    func updateTextStorage()
    {
        if let attributedText = attributedText
        {
            textStorage.setAttributedString(attributedText)
        }
        else if let text = text
        {
            textStorage.setAttributedString(NSAttributedString(string: text))
        }
        else
        {
            textStorage.setAttributedString(NSAttributedString(string: ""))
        }
        
        for r in linkRanges ?? [] {
            textStorage.addAttributes([NSAttributedStringKey.font: font,
                                       NSAttributedStringKey.foregroundColor: linkTextColor], range: r)
        }
    }
    
    //取出链接范围
    var linkRanges: [NSRange]?
    {
        //创建匹配项
        let patterns = ["[a-zA-Z]*://[a-zA-Z0-9/\\.]*", "#.*?#", "@[\\u4e00-\\u9fa5a-zA-Z0-9_-]*"]
        //创建正则表达式
        var ranges = [NSRange]()
        for pattern in patterns
        {
            guard let regx = try? NSRegularExpression(pattern: pattern, options: []) else
            {
                continue
            }
            let range = NSRange(location: 0, length: textStorage.length)
            let matchs = regx.matches(in: textStorage.string, options: [], range: range)
            
            for m in matchs
            {
                ranges.append(m.range(at: 0))
            }
        }
        return ranges
    }
}

private extension YYYTextKitLabel
{
    //选中文本的range
    func linkRangeAtLocation(_ location: CGPoint) -> NSRange?
    {
        if textStorage.length == 0
        {
            return nil
        }
        let index = layoutManager.glyphIndex(for: location, in: textContainer)
        
        for range in linkRanges ?? []
        {
            if NSLocationInRange(index, range)
            {
                return range
            }
        }
        return nil
    }
    //修改选中文本的背景
    func modifySelectedTextBackgroud(isSet: Bool)
    {
        if let range = selectedRange
        {
            if isSet
            {
                textStorage.addAttributes([NSAttributedStringKey.backgroundColor: selectedBackgroudColor], range: range)
            }
            else
            {
                textStorage.addAttributes([NSAttributedStringKey.backgroundColor: UIColor.clear], range: range)
                selectedRange = nil
            }
            setNeedsDisplay()
        }
    }
}
